matrix World;
matrix View;
matrix Projection;
matrix WorldIT;
matrix LightMatrix;
matrix matTexture;

float4 vecLightDir;
float4 LightPosition;
float4 vecEye;

float2  g_vSampleOffsets[15];
float   g_fSampleWeights[15];

struct VS_INPUT
{
	float4 Pos 		: POSITION;
	float4 Diff		: COLOR0;
	float4 Amb		: COLOR1; 
	float4 Spec		: COLOR2;
	float3 Nor 		: NORMAL;
	float2 Tex 		: TEXCOORD;   
};

struct VS_OUTPUT
{
	float4 Pos		: SV_POSITION;
	float4 Diffuse	: COLOR0;
	float4 Ambient	: COLOR1; 
	float4 Specular	: COLOR2;
	float3 Light	: TEXCOORD0;
	float3 Nor		: VS_NORMAL;
	float3 View		: TEXCOORD1;
};

struct PS_INPUT_SHADOW
{
	float4 Pos : SV_POSITION;
	float Depth : TEXCOORD0;
};

struct PS_INPUT_SCENE
{
	float4 Pos : SV_POSITION;
	float3 Normal : NORMAL0;
	float3 Tangent : NORMAL1;
	float3 Bitangent : NORMAL2;
	float4 Diffuse : COLOR0;
	float4 Ambient : COLOR1; 
	float4 Specular : COLOR2;
	float2 TexCoord : TEXCOORD0;
	float4 ProjCoord : TEXCOORD1;
	float4 ScreenCoord : TEXCOORD2;
	float3 LightVec : TEXCOORD4;
	float3 EyeVec : TEXCOORD5;
};

Texture2D tColorMap;
SamplerState ColorSampler
{

	Filter = Min_Mag_Mip_Linear;

	AddressU = Wrap;
	AddressV = Wrap;
};

Texture2D tShadowMap;
SamplerState ShadowSampler
{
	Filter = Min_Mag_Mip_Linear;

	AddressU = Clamp;
	AddressV = Clamp;
};

Texture2D tScreenMap;
SamplerState ScreenSampler
{

	Filter = Min_Mag_Mip_Linear;

	AddressU = Clamp;
	AddressV = Clamp;
};

Texture2D tNormalMap;
SamplerState NormalSampler
{
	Filter = Min_Mag_Mip_Linear;
    AddressU = Wrap;
    AddressV = Wrap;
	
};


void phong_shading(	PS_INPUT_SCENE IN,
	float3 LightColor,
	float3 Nn,
	float3 Ln,
	float3 Vn,
	out float3 DiffuseContrib,
	out float3 SpecularContrib)
{
	float3 Hn = normalize(Vn + Ln);
	float4 litV = lit(dot(Ln,Nn),dot(Hn,Nn),55);
	DiffuseContrib = litV.y * LightColor;
	SpecularContrib = litV.y * litV.z * 0.4 * LightColor;
}

//----------------------------------------------

PS_INPUT_SHADOW VS_Shadow( VS_INPUT input )
{
	PS_INPUT_SHADOW Out = (PS_INPUT_SHADOW)0;

	Out.Pos = mul(input.Pos, World);
	Out.Pos = mul(Out.Pos, LightMatrix);

	Out.Depth = Out.Pos.z;

	return Out;
}

float4 PS_Shadow( PS_INPUT_SHADOW In ) : SV_TARGET
{
	// Output the scene depth
	return float4( In.Depth, In.Depth, In.Depth, 1.0f );
}

PS_INPUT_SCENE VS_Scene( VS_INPUT input )
{
	PS_INPUT_SCENE Out = (PS_INPUT_SCENE)0;

	// Output the transformed position
	Out.Pos = mul(input.Pos, World);
	Out.Pos = mul(Out.Pos, View);
	Out.Pos = mul(Out.Pos, Projection);

	// Output the texture coordinates
	Out.TexCoord = input.Tex;


	// Output the projective texture coordinates
	Out.ProjCoord = mul(input.Pos, World);
	Out.ProjCoord = mul(Out.ProjCoord, matTexture );


	// Output the screen-space texture coordinates
	Out.ScreenCoord.x = ( Out.Pos.x * 0.5 + Out.Pos.w * 0.5 );
	Out.ScreenCoord.y = ( Out.Pos.w * 0.5 - Out.Pos.y * 0.5 );
	Out.ScreenCoord.z = Out.Pos.w;
	Out.ScreenCoord.w = Out.Pos.w;

	// Get the world space vertex position
	float4 WorldPos = mul( input.Pos, World );

	// Output the world space normal
	Out.Normal = normalize( mul( -input.Nor, WorldIT ) );
	
	float3 c1 = cross(Out.Normal, float3(0.0,0.0,1.0));
	float3 c2 = cross(Out.Normal, float3(0.0,1.0,0.0));
	
	if( length(c1)>length(c2) )
	{
		Out.Tangent = normalize(c1);
	}
	else
	{
		Out.Tangent = normalize(c2);
	}
	
	Out.Bitangent = normalize( cross(Out.Normal, Out.Tangent) );

	// Move the light vector into tangent space
	Out.LightVec = LightPosition.xyz - WorldPos.xyz;

	// Move the eye vector into tangent space
	Out.EyeVec = vecEye.xyz - WorldPos.xyz;

	// Colors passtrouh
	Out.Diffuse = input.Diff;
	Out.Ambient = input.Amb;
	Out.Specular = input.Spec;

	return Out;
}

float4 PS_ShadowHard( PS_INPUT_SCENE In ) : SV_TARGET
{
	// Normalize the normal, light and eye vectors
	In.Normal = normalize( In.Normal );
	In.LightVec = normalize( In.LightVec );
	In.EyeVec = normalize( In.EyeVec );

	float fShadowTerm = 0.0f;
	fShadowTerm = tShadowMap.Sample(ShadowSampler, In.ProjCoord.xyz / In.ProjCoord.w) < (In.ProjCoord.z - 0.001f) ? 0.1f : 1.0f;

	// Generate the 9 texture co-ordinates for a 3x3 PCF kernel
	float4 vTexCoords[9];

	// Texel size
	float fTexelSize = 1.0f / 2048.0f;

	// Generate the tecture co-ordinates for the specified depth-map size
	// 4 3 5
	// 1 0 2
	// 7 6 8
	
	vTexCoords[0] = In.ProjCoord;

	vTexCoords[1] = In.ProjCoord + float4( -fTexelSize, 0.0f, 0.0f, 0.0f );
	vTexCoords[2] = In.ProjCoord + float4( fTexelSize, 0.0f, 0.0f, 0.0f );
	vTexCoords[3] = In.ProjCoord + float4( 0.0f, -fTexelSize, 0.0f, 0.0f );
	vTexCoords[6] = In.ProjCoord + float4( 0.0f, fTexelSize, 0.0f, 0.0f );

	vTexCoords[4] = In.ProjCoord + float4( -fTexelSize, -fTexelSize, 0.0f, 0.0f );
	vTexCoords[5] = In.ProjCoord + float4( fTexelSize, -fTexelSize, 0.0f, 0.0f );
	vTexCoords[7] = In.ProjCoord + float4( -fTexelSize, fTexelSize, 0.0f, 0.0f );
	vTexCoords[8] = In.ProjCoord + float4( fTexelSize, fTexelSize, 0.0f, 0.0f );

	// Sample each of them checking whether the pixel under test is shadowed or not
	for( int i = 0; i < 9; i++ )
	{
		float A = tShadowMap.Sample(ShadowSampler, vTexCoords[i].xyz / vTexCoords[i].w).r; //tex2Dproj( ShadowSampler, vTexCoords[i] ).r;
		float B = In.ProjCoord.z - 0.001f;

		// Texel is shadowed
		fShadowTerm += A < B ? 0.0f : 1.0f;
	}

	// Get the average
	fShadowTerm = fShadowTerm / 9.0f;

	float3 diffContrib;
	float3 specContrib;
	float3 Ln = In.LightVec;
	float3 Vn = In.EyeVec;
	float3 Nn = In.Normal;
	float3 Tn = In.Tangent;
    float3 Bn = In.Bitangent;
	float BumpCoeff = 2.0; //wspolczynnik bumpa
	float3 bump = BumpCoeff * (tNormalMap.Sample(NormalSampler,In.TexCoord).rgb - float3(0.5,0.5,0.5));
    Nn = Nn + bump.x*Tn + bump.y*Bn;
    Nn = normalize(Nn);
	
	float3 Lamp0Color = {0.7,0.7,0.7};
	float3 AmbiColor = (In.Ambient.xyz)/9;
	phong_shading(In,Lamp0Color,Nn,Ln,Vn,diffContrib,specContrib);
	float3 diffuseColor = In.Diffuse;// = tex2D(ColorSampler,IN.UV).rgb;
	float3 result = specContrib*In.Specular.xyz*fShadowTerm+(diffuseColor.xyz*(diffContrib*fShadowTerm+AmbiColor));
	// return as float4
	return float4(result,1);
}

VS_OUTPUT VS1( VS_INPUT input ) // float4 Pos : POSITION, float3 Normal : NORMAL)
{
	float4 Pos = input.Pos;
	float3 Normal = -input.Nor;
	VS_OUTPUT Out; 
	Out.Pos = mul(input.Pos, World); // transform Position
	Out.Pos = mul(Out.Pos, View);
	Out.Pos = mul(Out.Pos, Projection);
	Out.Light = vecLightDir;              // L
	float3 PosWorld = normalize(mul(Pos, World).xyz); 
	Out.View = vecEye - PosWorld;         // V
	Out.Nor = mul(Normal, World);     // N
	Out.Diffuse = input.Diff;
	Out.Ambient = input.Amb;
	Out.Specular = input.Spec;

	return Out;
}


// Utility function for phong shading

void phong_shading2(	VS_OUTPUT IN,
	float3 LightColor,
	float3 Nn,
	float3 Ln,
	float3 Vn,
	out float3 DiffuseContrib,
	out float3 SpecularContrib)
{
	float3 Hn = normalize(Vn + Ln);
	float4 litV = lit(dot(Ln,Nn),dot(Hn,Nn),55);
	DiffuseContrib = litV.y * LightColor;
	SpecularContrib = litV.y * litV.z * 0.4 * LightColor;
}

float4 std_PS(VS_OUTPUT IN) : SV_Target {
	float3 diffContrib;
	float3 specContrib;
	float3 Ln = normalize(IN.Light);
	float3 Vn = normalize(IN.View);
	float3 Nn = normalize(IN.Nor);
	float3 Lamp0Color = {0.9,0.9,0.9};
	float3 AmbiColor = (IN.Ambient.xyz)/5;
	phong_shading2(IN,Lamp0Color,Nn,Ln,Vn,diffContrib,specContrib);
	float3 diffuseColor = IN.Diffuse;// = tex2D(ColorSampler,IN.UV).rgb;
	float3 result = specContrib+(diffuseColor.xyz*(diffContrib+AmbiColor));
	// return as float4
	return float4(result,1);
}

technique10 texturingDisabled
{
	pass P0
	{
		SetVertexShader( CompileShader( vs_4_0, VS1() ) );
		SetGeometryShader( NULL );
		SetPixelShader( CompileShader( ps_4_0, std_PS() ) );
	}
}

technique10 Shadow01
{
	pass P0
	{
		SetVertexShader( CompileShader( vs_4_0, VS_Shadow() ) );
		SetGeometryShader( NULL );
		SetPixelShader( CompileShader( ps_4_0, PS_Shadow() ) );
	}
}
technique10 ShadowHard
{
	pass P0
	{
		SetVertexShader( CompileShader( vs_4_0, VS_Scene() ) );
		SetGeometryShader( NULL );
		SetPixelShader( CompileShader( ps_4_0, PS_ShadowHard() ) );
	}
}
